package rewardsonline.rewards;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.util.UriComponentsBuilder;

import common.datetime.SimpleDate;
import common.money.MonetaryAmount;
import rewards.Dining;
import rewards.InvalidCreditCardException;
import rewards.RewardConfirmation;
import rewards.RewardNetwork;
import rewards.RewardNetworkException;
import rewards.admin.accounts.AccountManager;
import rewardsonline.accounts.Account;

/**
 * A controller for handling Rewards.
 */
@Controller
public class RewardController {

	// We need the reward service and the account service
	@Autowired
	RewardNetwork rewardNetwork;

	@Autowired
	AccountManager accountManager;

	// We also need a messaging-template for the lab
	@Autowired
	SimpMessagingTemplate messagingTemplate;

	/**
	 * Display the New Reward form.
	 * 
	 * @param model
	 *            Spring MVC model.
	 * @return Name of form view.
	 */
	@RequestMapping(value = "/rewards/new", method = RequestMethod.GET)
	public String newRewardForm(Model model) {
		model.addAttribute("dining", new DiningFormObject());
		return "rewards/create";
	}

	/**
	 * Handles the submission of the New Reward form to make the reward happen.
	 * 
	 * @param diningModel
	 *            The {@link DiningFormObject} form-model object that holds the
	 *            new reward details.
	 * @param url
	 *            The URL that was mapped to this handler.
	 * @return A reward confirmation.
	 * @throws RewardNetworkException
	 *             Unable to make the reward.
	 */
	@RequestMapping(value = "/rewards", method = RequestMethod.POST)
	public ResponseEntity<Void> newReward(DiningFormObject diningModel,
			@Value("#{request.requestURL}") StringBuffer url) throws RewardNetworkException {
		// This is only a test system, so the Merchant Number is not set in the
		// DiningFormObject. Fortunately there is only one merchant in our test
		// database.
		String merchantId = "1234567890";

		// Create a Dining event from the DiningFormObject DTO
		Dining dining = new Dining(new MonetaryAmount(diningModel.getAmount()), diningModel.getCreditCardNumber(),
				merchantId, SimpleDate.today());

		// Issue the reward
		RewardConfirmation confirmation = rewardNetwork.rewardAccountFor(dining);

		// Return the confirmation id
		String rewardConfirmationId = confirmation.getConfirmationNumber();

		// Note the use of the ResponseEntity builder API (introduced since
		// Spring 4.1)
		return ResponseEntity
				.created(UriComponentsBuilder.fromHttpUrl(url.toString()).pathSegment("{id}")
						.buildAndExpand(rewardConfirmationId).toUri())
				.header("RewardConfirmationId", rewardConfirmationId).build();

	}

	/**
	 * Handler for messages sent to <code>/topic/rewards</code> channel.
	 * Whenever a message is received, it looks up the account indicated by the
	 * credit-card number. Then:
	 * <ul>
	 * <li>Send the current state of the account (including the updated
	 * beneficiaries) to <code>/topic/accounts.{accountNumber}</code>.
	 * <li>Send the account-number to <code>/topic/rewards</code>.
	 * </ul>
	 * 
	 * To see how these messages are handled look at <code>show.jsp</code>
	 * and <code>list.jsp</code> respectively.
	 * 
	 * @param creditCardNumber
	 *            The credit-card used to generate a new reward.
	 */
	@MessageMapping("/rewards")
	@SendTo("/topic/rewards")
	public String reward(String creditCardNumber) throws InvalidCreditCardException {
		Account account = accountManager.findByCreditCard(creditCardNumber);
		messagingTemplate.convertAndSend("/topic/accounts." + account.getNumber(), account);
		return account.getNumber();
	}

}
